package com.stud.common.utils;

import cn.hutool.core.date.DateField;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import com.stud.common.core.domain.Cycle;
import com.stud.common.enums.CycleEnum;
import com.stud.common.enums.XTypeEnum;
import com.stud.common.exception.ServiceException;
import com.stud.common.jdbc.MyJdbcTemplate;
import com.stud.common.utils.spring.SpringUtils;
import com.google.common.collect.Lists;

import java.text.DecimalFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.Objects;

/**
 * @author Stud
 * @title XXXXX
 * @desription XXXXX
 * @data 2022-09-27 9:39
 */
public class CycleUtil {
    public static final String NORM_DATETIME_HOUR_PATTERN = "yyyy-MM-dd HH";
    public static final int X_TYPE_HOUR = 0;
    public static final int X_TYPE_DAY = 1;
    public static final int X_TYPE_MONTH = 2;
    public static final int X_TYPE_SEASON = 3;
    private static final int HOUR = 24;

    public static Cycle cycle(CycleEnum cycleEnum) {
        if (cycleEnum == null) {
            throw new ServiceException("cycleEnum不能为空");
        }
        Calendar calendar = Calendar.getInstance();
        long timeMillis = calendar.getTimeInMillis();
        Date now = DateUtil.dateNew(new Date(timeMillis));
        Date end = DateUtil.dateNew(new Date(timeMillis + 1800000));
        Cycle cycle = new Cycle();
        switch (cycleEnum) {
            case HOUR24:
                cycle.setStart(DateUtil.offsetHour(now, -24));
                cycle.setEnd(end);
                break;
            case HOUR48:
                cycle.setStart(DateUtil.offsetHour(now, -48));
                cycle.setEnd(end);
                break;
            case HOUR72:
                cycle.setStart(DateUtil.offsetHour(now, -72));
                cycle.setEnd(end);
                break;
            case DAY7:
                cycle.setStart(DateUtil.offsetDay(now, -7));
                cycle.setEnd(end);
                break;
            case DAY30:
                cycle.setStart(DateUtil.offsetDay(now, -30));
                cycle.setEnd(end);
                break;
            case TODAY:
                calendar.set(Calendar.HOUR_OF_DAY, 0);
                calendar.set(Calendar.MINUTE, 0);
                calendar.set(Calendar.SECOND, 0);
                calendar.set(Calendar.MILLISECOND, 0);
                cycle.setStart(new DateTime(calendar.getTime()));
                cycle.setEnd(end);
                break;
            case YESTERDAY:
                calendar.set(Calendar.HOUR_OF_DAY, 0);
                calendar.set(Calendar.MINUTE, 0);
                calendar.set(Calendar.SECOND, 0);
                calendar.set(Calendar.MILLISECOND, 0);
                cycle.setEnd(new DateTime(calendar.getTime()));
                calendar.add(Calendar.DAY_OF_YEAR, -1);
                cycle.setStart(new DateTime(calendar.getTime()));
                break;
            case TOMORROW:
                calendar.set(Calendar.HOUR_OF_DAY, 0);
                calendar.set(Calendar.MINUTE, 0);
                calendar.set(Calendar.SECOND, 0);
                calendar.set(Calendar.MILLISECOND, 0);
                calendar.add(Calendar.DAY_OF_YEAR, 1);
                cycle.setStart(new DateTime(calendar.getTime()));
                calendar.add(Calendar.DAY_OF_YEAR, 1);
                cycle.setEnd(new DateTime(calendar.getTime()));
                break;
            case WEEK:
                cycle.setStart(DateUtil.beginOfWeek(now));
                cycle.setEnd(end);
                break;
            case MONTH:
                cycle.setStart(DateUtil.beginOfMonth(now));
                cycle.setEnd(end);
                break;
            case SEASON:
                cycle.setStart(DateUtil.beginOfQuarter(now));
                cycle.setEnd(end);
                break;
            case YEAR:
                cycle.setStart(DateUtil.beginOfYear(now));
                cycle.setEnd(end);
                break;
            default:
                throw new ServiceException("不支持该周期类型");
        }
        return cycle;
    }

    public static List<Cycle> cycleList(CycleEnum cycleEnum, XTypeEnum xTypeEnum) {
        Cycle cycle = cycle(cycleEnum);
        return cycleList(cycle, xTypeEnum, false, false);
    }

    public static List<Cycle> cycleList(Cycle cycle, XTypeEnum xTypeEnum) {
        return cycleList(cycle, xTypeEnum, false, false);
    }

    /**
     * @param cycle
     * @param xTypeEnum
     * @param dateFlag       是否塞入开始结束时间
     * @param timeMillisFlag 是否塞入开始结束时间戳
     * @return
     */
    public static List<Cycle> cycleList(Cycle cycle, XTypeEnum xTypeEnum, boolean timeMillisFlag, boolean dateFlag) {
        List<Cycle> list = Lists.newArrayList();
        long startMillis = cycle.getStart().getTime();
        long endMillis = cycle.getEnd().getTime();
        DateTime date = DateUtil.date(cycle.getStart());
        switch (xTypeEnum) {
            case MINUTE:
                while (startMillis <= endMillis) {
                    list.add(new Cycle(DateUtil.format(new Date(startMillis), DatePattern.NORM_DATETIME_MINUTE_PATTERN)));
                    startMillis += 60000;
                }
                break;
            case HOUR:
                while (startMillis <= endMillis) {
                    list.add(new Cycle(DateUtil.format(new Date(startMillis), NORM_DATETIME_HOUR_PATTERN)));
                    startMillis += 3600000;
                }
                break;
            case DAY:
                while (startMillis <= endMillis) {
                    Cycle tempCycle = new Cycle(DateUtil.format(new Date(startMillis), DatePattern.NORM_DATE_PATTERN));
                    long endTimeInMillis = startMillis + 86400000;

                    if (timeMillisFlag) {
                        tempCycle.setStartTimeInMillis(startMillis);
                        tempCycle.setEndTimeInMillis(endTimeInMillis);
                    }

                    if (dateFlag) {
                        tempCycle.setStart(new Date(startMillis));
                        tempCycle.setEnd(new Date(endTimeInMillis));
                    }

                    list.add(tempCycle);
                    startMillis = endTimeInMillis;
                }
                break;
            case WEEK://为保证和数据库一致,开始结束以数据库为准
                MyJdbcTemplate jdbcTemplate = SpringUtils.getBean(MyJdbcTemplate.class);
                String dbResult = jdbcTemplate.findSingleColumn(" select date_format('" + DateUtil.formatDate(date) + "', '%x-%v') ");
                String[] dbYearWeek = dbResult.split("-");
                int startYear = Toolkit.object2Int(dbYearWeek[0]);
                int startWeek = Toolkit.object2Int(dbYearWeek[1]);

                DecimalFormat decimalFormat = new DecimalFormat("00");

                endMillis = DateUtil.endOfWeek(cycle.getEnd()).getTime() + 1;
                while (startMillis <= endMillis) {
                    list.add(new Cycle(startYear + "-" + decimalFormat.format(startWeek)));
                    startWeek += 1;
                    if (startWeek == 52) {
                        dbResult = jdbcTemplate.findSingleColumn(" select date_format('" + DateUtil.formatDate(new Date(startMillis)) + "', '%x-%v') ");
                        dbYearWeek = dbResult.split("-");
                        startYear = Toolkit.object2Int(dbYearWeek[0]);
                        startWeek = Toolkit.object2Int(dbYearWeek[1]);
                    }
                    if (startWeek == 53) {
                        startWeek = 1;
                        startYear += 1;
                    }
                    startMillis += 604800000;
                }
                break;
            case MONTH:
                while (date.getTime() <= endMillis) {
                    list.add(new Cycle(DateUtil.format(date, DatePattern.NORM_MONTH_PATTERN)));
                    date = DateUtil.offsetMonth(date, 1);
                }
                break;
            case SEASON:
                while (date.getTime() <= endMillis) {
                    list.add(new Cycle(DateUtil.year(date) + "-" + DateUtil.quarter(date)));
                    date = DateUtil.offsetMonth(date, 3);
                }
                break;
            case YEAR:
                while (date.getTime() <= endMillis) {
                    list.add(new Cycle(String.valueOf(DateUtil.year(date))));
                    date = DateUtil.offset(date, DateField.YEAR, 1);
                }
                break;
            default:
                throw new ServiceException("不支持该X轴类型");
        }

        return list;
    }


    public static List<String> cycleDayHour(Date date) {
        String day = DateUtil.formatDate(date);

        List<String> list = Lists.newArrayList();

        for (int i = 0; i < HOUR; i++) {
            list.add(day + " " + prefix0(i));
        }
        return list;
    }

    public static List<String> cycle24Hour(Date date) {
        List<String> list = Lists.newArrayList();
        for (int i = HOUR - 1; i >= 0; i--) {
            list.add(DateUtil.format(DateUtil.offsetHour(date, 0 - i), "yyyy-MM-dd HH"));
        }
        return list;
    }

    public static List<String> cycleSeason(Date start, Date end) {
        DateTime temp = DateUtil.dateNew(start);
        List<String> list = Lists.newArrayList();
        while (temp.compareTo(end) < 0) {
            list.add(DateUtil.formatDate(temp));
            temp.offset(DateField.DAY_OF_YEAR, 1);
        }
        return list;
    }

    public static List<String> cycleMonth(Date start, Date end) {
        DateTime temp = DateUtil.dateNew(start);
        List<String> list = Lists.newArrayList();
        while (temp.compareTo(end) < 0) {
            list.add(DateUtil.format(temp, DatePattern.NORM_MONTH_PATTERN));
            temp.offset(DateField.MONTH, 1);
        }
        return list;
    }

    public static List<String> cycleDay(Date start, Date end) {
        DateTime temp = DateUtil.dateNew(start);
        List<String> list = Lists.newArrayList();
        while (temp.compareTo(end) < 0) {
            list.add(DateUtil.formatDate(temp));
            temp.offset(DateField.DAY_OF_YEAR, 1);
        }
        return list;
    }

    public static List<String> cycleHour(Date start, Date end) {
        DateTime temp = DateUtil.dateNew(start);
        List<String> list = Lists.newArrayList();
        while (temp.compareTo(end) < 0) {
            list.add(formatTime(temp, X_TYPE_HOUR));
            temp.offset(DateField.HOUR, 1);
        }
        return list;
    }

    public static String formatTime(Date date, int xType) {
        switch (xType) {
            case X_TYPE_HOUR:
                return DateUtil.format(date, "yyyy-MM-dd HH");
            case X_TYPE_DAY:
                return DateUtil.format(date, "yyyy-MM-dd");
            case X_TYPE_MONTH:
                return DateUtil.format(date, "yyyy-MM");
            case X_TYPE_SEASON:
                Calendar calendar = DateTime.of(date).toCalendar();
                int year = calendar.get(Calendar.YEAR);
                int quarter = calendar.get(Calendar.MONTH) / 3 + 1;
                return String.format("%d-第%d季度", year, quarter);
            default:
                return null;
        }
    }

    /**
     * 获取整点(小时)
     *
     * @param date
     * @return
     */
    public static Date unitHourDate(Date date) {
        long timeMillis = Objects.isNull(date) ? System.currentTimeMillis() : date.getTime();
        timeMillis = timeMillis - (timeMillis % 3600000);
        return new Date(timeMillis);
    }

    /**
     * 获取整点时间戳(小时)
     *
     * @param timeMillis
     * @return
     */
    public static Long unitHourTimeMillis(Long timeMillis) {
        if (Objects.isNull(timeMillis)) {
            timeMillis = System.currentTimeMillis();
        }
        timeMillis = timeMillis - (timeMillis % 3600000);
        return timeMillis;
    }

    private static String prefix0(int num) {
        return num < 10 ? "0" + num : "" + num;
    }
}
